import { _decorator, Component, Node, Prefab, instantiate, Vec3 } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('ObjectPool')
export class ObjectPool extends Component {
    private static _instance: ObjectPool = null;
    private _pools: Map<string, Node[]> = new Map();
    private _prefabs: Map<string, Prefab> = new Map();
    
    // Singleton pattern
    public static get instance(): ObjectPool {
        return this._instance;
    }
    
    onLoad() {
        // Establish singleton
        if (ObjectPool._instance === null) {
            ObjectPool._instance = this;
            this.node.parent.addChild(this.node);
        } else {
            this.node.destroy();
            return;
        }
    }
    
    /**
     * Register a prefab to be used with this pool
     * @param name Unique name for this prefab pool
     * @param prefab Prefab asset to use for instantiation
     * @param initialSize Initial size of the pool (optional)
     */
    public registerPrefab(name: string, prefab: Prefab, initialSize: number = 0): void {
        if (this._prefabs.has(name)) {
            console.warn(`Prefab with name ${name} already registered in ObjectPool`);
            return;
        }
        
        this._prefabs.set(name, prefab);
        this._pools.set(name, []);
        
        // Pre-populate the pool if initial size > 0
        for (let i = 0; i < initialSize; i++) {
            const node = this.createObject(name);
            this.returnObject(name, node);
        }
    }
    
    /**
     * Get an object from the pool
     * @param name Name of the prefab pool
     * @param position (Optional) Position to set for the object
     * @param parent (Optional) Parent node to attach to
     * @returns Node from pool or newly instantiated
     */
    public getObject(name: string, position?: Vec3, parent?: Node): Node {
        if (!this._pools.has(name)) {
            console.warn(`Object pool for ${name} has not been registered`);
            return null;
        }
        
        let node: Node = null;
        const pool = this._pools.get(name);
        
        // If pool is empty, create a new object
        if (pool.length === 0) {
            node = this.createObject(name);
        } else {
            node = pool.pop();
        }
        
        // Set position if provided
        if (position) {
            node.setPosition(position);
        }
        
        // Set parent if provided, otherwise add to scene
        if (parent) {
            node.parent = parent;
        } else {
            node.parent = this.node;
        }
        
        node.active = true;
        
        return node;
    }
    
    /**
     * Return an object to the pool
     * @param name Name of the prefab pool
     * @param node Node to return to the pool
     */
    public returnObject(name: string, node: Node): void {
        if (!this._pools.has(name)) {
            console.warn(`Object pool for ${name} has not been registered`);
            return;
        }
        
        node.parent = null;
        node.active = false;
        
        const pool = this._pools.get(name);
        pool.push(node);
    }
    
    /**
     * Create a new object from the registered prefab
     * @param name Name of the prefab pool
     * @returns Newly instantiated node
     */
    private createObject(name: string): Node {
        const prefab = this._prefabs.get(name);
        if (!prefab) {
            console.error(`Prefab with name ${name} not found in ObjectPool`);
            return null;
        }
        
        return instantiate(prefab);
    }
    
    /**
     * Clear all objects from a specific pool
     * @param name Name of the prefab pool
     */
    public clearPool(name: string): void {
        if (!this._pools.has(name)) {
            return;
        }
        
        const pool = this._pools.get(name);
        pool.forEach(node => {
            node.destroy();
        });
        
        pool.length = 0;
    }
    
    /**
     * Clear all pools
     */
    public clearAllPools(): void {
        this._pools.forEach((pool, name) => {
            this.clearPool(name);
        });
    }
    
    onDestroy() {
        this.clearAllPools();
    }
} 